/*
Kakine
ActionScript Framework
Copyright © 2011, PUNY, LLC.

This file is part of KAKINE.

KAKINE is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

KAKINE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with KAKINE.  If not, see <http://www.gnu.org/licenses/>.
*/ 
package com.puny.utils
{
	/**
	 * $Revision: 380 $<br/>
	 * A calss To compute four arithmetic expressions.
	 * @example
	 * <listing>
	 * var num:Number = ExpressionParser.calculate("1-4");
	 * // num = -3
	 * </listing>
	 *
	 * <listing>
	 * var num:Number = ExpressionParser.calculate("2~~(5+3)");
	 * // num = 16
	 * </listing>
	 */
	public class ExpressionParser
	{
		public static function calculate(expression:String):Number {
			var reg:RegExp = /(\+|\-|\*|\/|\(|\))/
			var original:Array = expression.split(reg);
			var tokens:Array = new Array();
			for each(var token:String in original) {
				if (token != "") {
					tokens.push(token);
				}
			}
			return parser(tokens);
		}
		
		private static function parser(tokens:Array):Number {
			var left:Number = getValue(tokens);
			var operand:Function;
			var right:Number;
			while(tokens.length > 0) {
				operand = getOperand(tokens);
				if (operand == null) {
					break;
				}
				right = getValue(tokens,true);
				left = operand(left,right);
			}
			
			return left;
		}
		
		private static function getOperand(tokens:Array):Function {
			var token:String = tokens.shift();
			var fnc:Function;
			switch (token) {
				case "+":
					fnc = add;
					break;
				case "-":
					fnc = sub;
					break;
				case "*":
					fnc = mul;
					break;
				case "/":
					fnc = div;
					break;
				case ")":
					tokens.unshift(token);
				case null:
					return null;
				default:
					throw SyntaxError("Syntax Error around " + token);
					break;
			}
			return fnc;
		}
		private static function add(left:Number, right:Number):Number {
			return left + right;
		} 		
		private static function sub(left:Number, right:Number):Number {
			return left - right;
		} 		
		private static function mul(left:Number, right:Number):Number {
			return left * right;
		} 		
		private static function div(left:Number, right:Number):Number {
			return left / right;
		} 		
		private static function getValue(tokens:Array,right:Boolean = false):Number {
			var token:String = tokens.shift();
			var operand:String = tokens.shift();
			if (right && (operand == "*" || operand == "/")) {
				tokens.unshift(operand);
				tokens.unshift(token);
				return parser(tokens);
			}
			tokens.unshift(operand);
			
			var num:Number;
			switch (token) {
				case "-":
					tokens.unshift(token);
					return 0;
				case "(":
					num = parser(tokens);
					if (tokens[0] != ")") {
						throw SyntaxError(") is missing.");
						break;
					}
					return num;
				case "+":
				case ")":
				case "*":
				case "/":
					throw SyntaxError("Syntax Error around " + token);
					break;
				case null:
					throw SyntaxError("Syntax Error at the end.");
					break;
				default:
					return Number(token);			
			}
			return 0;
		}
	}
}